import React, { useCallback, useRef } from "react";
import { connect } from "react-redux";
import { useDrag, useDrop } from "react-dnd";
import classnames from "classnames";
import { IState } from "store/reducers/cms";
import { SET_CMS_CURRENT_NODE, UPDATE_CMS_COMPONENT } from "store/constants";
import "./index.scss";

interface DragItemProps {
    type: string;
    id: string;
}
interface ICMSCompWrapperProps {
    id: string;
    setCurrentNode: (payload: any) => void;
    updateComponentTree: (payload: any) => void;
    isWrapper: boolean;
    children?: any;
    cms: IState;
}
const BaseCMSCompWrapper: React.FC<ICMSCompWrapperProps> = (props) => {
    const { id, setCurrentNode, isWrapper, cms } = props;
    const wrapperClass = classnames("cms-component-wrapper", {
        "active-wrapper": Array.isArray(cms.currentNode) ? cms.currentNode[cms.currentNode.length - 1] === id : cms.currentNode === id
    })
    const findNode = useCallback((list: any[], id: string, path = []) => {
        const node = list.find(item => item?.id === id);
        if(node) return [...path, id];
        for(let idx = 0; idx < list.length; idx++){
            const item = list[idx];
            const res: any[] = findNode(item.children, id, [...path, item.id]);
            if(res[res?.length - 1] === id) return res;
        }
        return [];
    }, [])
    const getCurrentNodeId = () => {
        if(isWrapper) return id;
        // 查找当前节点的路径
        return findNode(cms.config, id, []);
    }
    const getNodeIndex = (sourceId: string, targetId: string) => {
        const { config, currentNode } = cms;
        if(Array.isArray(currentNode)){
            const curConfig = config.find(item => item.id === currentNode?.[0]);
            if(!curConfig) return [-1, -1];
            const configList = curConfig.children || [];
            const sourceIndex = configList.findIndex(child => child.id === sourceId);
            const targetIndex = configList.findIndex(child => child.id === targetId);
            return [sourceIndex, targetIndex];
        } else {
            const sourceIndex = config.findIndex(wrapper => wrapper.id === sourceId);
            const targetIndex = config.findIndex(wrapper => wrapper.id === targetId);
            return [sourceIndex, targetIndex];
        }
    }
    const sortNewConfig = (sourceIndex: number, targetIndex: number) => {
        if(sourceIndex === -1 || targetIndex === -1) return;
        const { config, currentNode } = cms;
        if(Array.isArray(currentNode)){  // 组件内部的位置移动
            const curConfig = config.find(item => item.id === currentNode?.[0]);
            if(!curConfig) return;
            const configList = curConfig.children || [];
            configList[sourceIndex] = configList.splice(targetIndex, 1, configList[sourceIndex])[0];  //交换souce和target的位置
            curConfig.children = [...configList];
            props.updateComponentTree([...config]);
        } else {  // 外层wrapper组件之间的位置变动
            config[sourceIndex] = config.splice(targetIndex, 1, config[sourceIndex])[0];
            props.updateComponentTree([...config]);
        }
    }
    const wrapperRef = useRef(null);
    const [, drop] = useDrop<DragItemProps, any, any>({
        accept: "comp-wrapper",
        hover(item, monitor){
            if (!wrapperRef.current) { 
                return
            }
            const dragId = item.id;
            const hoverId = id;
            if(dragId === hoverId) return;
            const [sourceIndex, targetIndex] = getNodeIndex(dragId, hoverId);
            const hoverBoundingRect = (wrapperRef.current as any).getBoundingClientRect();   
            const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
            const clientOffset = monitor.getClientOffset();
            if(!clientOffset) return;
            const hoverClientY = clientOffset.y - hoverBoundingRect.top;
            if (sourceIndex < targetIndex && hoverClientY < hoverMiddleY) return;
            if(sourceIndex > targetIndex  && hoverClientY > hoverMiddleY) return;
            sortNewConfig(sourceIndex, targetIndex);
        }
    })
    const [{ isDragging }, drag] = useDrag<DragItemProps, any, any>({
        type: "comp-wrapper",
        item: { type: "comp-wrapper", id },
        collect: (monitor) => ({
            isDragging: monitor.isDragging(),
        })
    })
    const opacity = isDragging ? 0 : 1;
    drag(drop(wrapperRef))
    return <div className={wrapperClass} onClick={(e) => {
        e.stopPropagation();
        setCurrentNode(getCurrentNodeId());
    }} ref={wrapperRef} style={{opacity, cursor: "pointer"}}>
        {props.children}
    </div>
}

const CMSCompWrapper = connect((state: any) => {
    return {cms: state.cms}
}, (dispatch: any) => {
    return {
        setCurrentNode: (payload: any) => dispatch({type: SET_CMS_CURRENT_NODE, payload}),
        updateComponentTree: (payload: any) => dispatch({type: UPDATE_CMS_COMPONENT, payload})
    }
})(BaseCMSCompWrapper);

export default CMSCompWrapper;

export const CMSCompWrapperHOC = (config: any) => {
    const { id, isWrapper } = config;
    return (props: any) => {
        return <CMSCompWrapper id={id} isWrapper={isWrapper}>
            {props.children}
        </CMSCompWrapper>
    }
}